import { BaseService } from '@/core/base.service';
import { SPUListDto, SPUQueryDto } from '@/dtos';
import { getCode } from '@/utils';
import { Injectable } from '@nestjs/common';
import { Prisma, SPU } from '@prisma/client';
import dayjs from 'dayjs';

@Injectable()
export class SPUService extends BaseService {
  async list({ params, sort }: SPUListDto) {
    const { current = 1, pageSize = 20, keyword, ...where } = params;

    let sql = `
            SELECT #{fields} FROM \`SPU\` AS t
            LEFT JOIN \`Category\` AS cc ON cc.id = t.categoryId
            LEFT JOIN \`Publisher\` AS p ON p.id = t.publisherId
            LEFT JOIN \`Creator\` AS c ON c.id = t.creatorId
            WHERE t.isDeleted = false
                AND t.isEnabled = true
                ${!!where.title ? `AND t.title LIKE '%${where.title}%'` : ''}
                ${!!where.categoryId ? `AND t.categoryId = ${where.categoryId}` : ''}
                ${!!where.publisherId ? `AND t.publisherId = ${where.publisherId}` : ''}
                ${!!where.creatorId ? `AND t.creatorId = ${where.creatorId}` : ''}
        `;

    const [{ count }] = await this.prisma.$queryRawUnsafe<[{ count: number }]>(
      sql.replace('#{fields}', 'COUNT(t.id) AS count'),
    );

    const list = await this.prisma.$queryRawUnsafe<SPU[]>(
      `${sql.replace(
        '#{fields}',
        `
          t.id, t.title, t.image, t.price, t.detail,
          t.startSaleNum, t.stock, t.isHideStock, t.isUseSku,
          t.categoryId, cc.title AS categoryTitle, 
          p.id AS publisherId, p.name AS publisherName, p.logo AS publisherLogo,
          c.id AS creatorId, c.name AS creatorName, c.logo AS creatorLogo
        `,
      )}
            ORDER BY ${
              !!sort && Object.keys(sort).length > 0
                ? Object.entries(sort)
                    .map(
                      ([k, v]) => `t.${k} ${v === 'descend' ? 'DESC' : 'ASC'}`,
                    )
                    .join(',')
                : 't.sort DESC'
            }
            LIMIT ${pageSize} 
            OFFSET ${(current - 1) * pageSize}
        `,
    );

    const total = count ? Number(count) : 0;
    const totalPage = Math.ceil(total / pageSize);
    const hasMore = current < totalPage;

    return {
      total,
      totalPage,
      hasMore,
      current,
      pageSize,
      list: list.map((o) => ({
        ...o,
        price: Number(o.price),
      })),
    };
  }

  async query({ params, sort }: SPUQueryDto) {
    const { current = 1, pageSize = 20, keyword, ...where } = params;

    let sql = `
          SELECT #{fields} FROM \`SPU\` AS t
          LEFT JOIN \`Category\` AS c ON c.id = t.categoryId
          WHERE t.isDeleted = false
              ${!!where.title ? `AND t.title LIKE '%${where.title}%'` : ''}
              ${!!where.categoryId ? `AND t.categoryId = ${where.categoryId}` : ''}
              ${!!where.publisherId ? `AND t.publisherId = ${where.publisherId}` : ''}
              ${!!where.creatorId ? `AND t.creatorId = ${where.creatorId}` : ''}
              ${!!where.type ? `AND c.type = ${where.type}` : ''}
        `;

    const [{ count }] = await this.prisma.$queryRawUnsafe<[{ count: number }]>(
      sql.replace('#{fields}', 'COUNT(t.id) AS count'),
    );

    const list = await this.prisma.$queryRawUnsafe<SPU[]>(
      `${sql.replace(
        '#{fields}',
        `
          t.id, t.title, t.image, t.price, t.detail, t.sort, t.isEnabled, 
          t.startSaleNum, t.stock, t.isHideStock, t.isUseSku, t.categoryId, 
          c.title AS categoryTitle, c.type, t.publisherId, t.creatorId           
        `,
      )}
            ORDER BY ${
              !!sort && Object.keys(sort).length > 0
                ? Object.entries(sort)
                    .map(
                      ([k, v]) => `t.${k} ${v === 'descend' ? 'DESC' : 'ASC'}`,
                    )
                    .join(',')
                : 't.createdAt DESC'
            }
            LIMIT ${pageSize} 
            OFFSET ${(current - 1) * pageSize}
        `,
    );

    const total = count ? Number(count) : 0;
    const totalPage = Math.ceil(total / pageSize);
    const hasMore = current < totalPage;

    return {
      total,
      totalPage,
      hasMore,
      current,
      pageSize,
      list: list.map((o) => ({
        ...o,
        price: Number(o.price),
      })),
    };
  }

  async findOne(
    where: Prisma.SPUWhereUniqueInput,
    select?: Prisma.SPUSelect,
  ): Promise<SPU | null> {
    return this.prisma.sPU.findUnique({
      where,
      select,
    });
  }

  async create(data: Prisma.SPUCreateInput): Promise<SPU> {
    return await this.prisma.sPU.create({
      data: {
        ...data,
      },
    });
  }

  async update(params: {
    where: Prisma.SPUWhereUniqueInput;
    data: Prisma.SPUUpdateInput;
  }): Promise<SPU> {
    const { where, data } = params;
    return this.prisma.sPU.update({
      where,
      data: {
        ...data,
      },
    });
  }

  async generateOrderNo() {
    return `${dayjs().format('YYYYMMDDHHmmss')}${await getCode(11)}`;
  }
}
